/*
 * Copyright 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.compose.ui.text.font

import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.text.internal.requirePrecondition
import androidx.compose.ui.util.lerp

/**
 * The thickness of the glyphs, in a range of [1, 1000].
 *
 * @param weight Font weight value in the range of [1, 1000]
 * @see Font
 * @see FontFamily
 */
@Immutable
class FontWeight(val weight: Int) : Comparable<FontWeight> {

    companion object {
        /** [Thin] */
        @Stable val W100 = FontWeight(100)
        /** [ExtraLight] */
        @Stable val W200 = FontWeight(200)
        /** [Light] */
        @Stable val W300 = FontWeight(300)
        /** [Normal] / regular / plain */
        @Stable val W400 = FontWeight(400)
        /** [Medium] */
        @Stable val W500 = FontWeight(500)
        /** [SemiBold] */
        @Stable val W600 = FontWeight(600)
        /** [Bold] */
        @Stable val W700 = FontWeight(700)
        /** [ExtraBold] */
        @Stable val W800 = FontWeight(800)
        /** [Black] */
        @Stable val W900 = FontWeight(900)

        /** Alias for [W100] */
        @Stable val Thin = W100
        /** Alias for [W200] */
        @Stable val ExtraLight = W200
        /** Alias for [W300] */
        @Stable val Light = W300
        /** The default font weight - alias for [W400] */
        @Stable val Normal = W400
        /** Alias for [W500] */
        @Stable val Medium = W500
        /** Alias for [W600] */
        @Stable val SemiBold = W600
        /** A commonly used font weight that is heavier than normal - alias for [W700] */
        @Stable val Bold = W700
        /** Alias for [W800] */
        @Stable val ExtraBold = W800
        /** Alias for [W900] */
        @Stable val Black = W900

        /** A list of all the font weights. */
        internal val values: List<FontWeight> =
            listOf(W100, W200, W300, W400, W500, W600, W700, W800, W900)
    }

    init {
        requirePrecondition(weight in 1..1000) {
            "Font weight can be in range [1, 1000]. Current value: $weight"
        }
    }

    override operator fun compareTo(other: FontWeight): Int {
        return weight.compareTo(other.weight)
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is FontWeight) return false
        if (weight != other.weight) return false
        return true
    }

    override fun hashCode(): Int {
        return weight
    }

    override fun toString(): String {
        return "FontWeight(weight=$weight)"
    }
}

/**
 * Linearly interpolate between two font weights.
 *
 * The [fraction] argument represents position on the timeline, with 0.0 meaning that the
 * interpolation has not started, returning [start] (or something equivalent to [start]), 1.0
 * meaning that the interpolation has finished, returning [stop] (or something equivalent to
 * [stop]), and values in between meaning that the interpolation is at the relevant point on the
 * timeline between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and 1.0, so
 * negative values and values greater than 1.0 are valid (and can easily be generated by curves).
 *
 * Values for [fraction] are usually obtained from an [Animation<Float>], such as an
 * `AnimationController`.
 */
fun lerp(start: FontWeight, stop: FontWeight, fraction: Float): FontWeight {
    val weight = lerp(start.weight, stop.weight, fraction).coerceIn(1, 1000)
    return FontWeight(weight)
}
